#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <memory>
#include <stdexcept>
#include <iomanip>
#include <ctime>
#include <algorithm>
#include <mutex>
#include <mpi.h>

/**
 * @brief Logging service class providing various levels of logging functionality
 * 
 * This class implements a comprehensive logging system with multiple verbosity levels,
 * output destinations, and message types (info, warning, error, fatal).
 */
class LoggingService {
public:
    /// Verbosity level enumeration
    enum class Verbosity {
        SILENT = 0,
        NORMAL = 1,
        NOISY = 2
    };

    /**
     * @brief Initialize the logging service
     * @param inputFile Base name for output files
     * @param isIOP Whether this process is the I/O process
     * 
     * Sets up log files and initializes the logging system. Must be called before any logging.
     */
    static void initialize(const std::string& inputFile, bool isIOP) {
        std::lock_guard<std::mutex> lock(mutex_);
        
        if (initialized_) {
            throw std::runtime_error("LoggingService already initialized");
        }

        isIOP_ = isIOP;
        prefix_ = inputFile.substr(0, inputFile.find_last_of('.'));

        if (isIOP_) {
            // Open stdout and log file
            logUnits_.push_back(&std::cout);
            
            std::string logFileName = prefix_ + ".out";
            logFile_.open(logFileName);
            if (!logFile_.is_open()) {
                throw std::runtime_error("Failed to open log file: " + logFileName);
            }
            logUnits_.push_back(&logFile_);
        }

        verbosity_ = Verbosity::NORMAL;
        initialized_ = true;
    }

    /**
     * @brief Finalize the logging service
     * 
     * Closes log files and cleans up resources.
     */
    static void finalize() {
        std::lock_guard<std::mutex> lock(mutex_);
        
        if (logFile_.is_open()) {
            logFile_.close();
        }
        logUnits_.clear();
        initialized_ = false;
    }

    /**
     * @brief Set the verbosity level
     * @param verbosity New verbosity level
     */
    static void setVerbosity(Verbosity verbosity) {
        std::lock_guard<std::mutex> lock(mutex_);
        verbosity_ = verbosity;
    }

    // Info logging methods
    static void info(const std::string& message, Verbosity verbosity = Verbosity::NORMAL) {
        logMessage("", message, verbosity);
    }

    static void info(const std::vector<std::string>& messages, bool clearMessages = false, 
                    Verbosity verbosity = Verbosity::NORMAL) {
        logMessages("", messages, clearMessages, verbosity);
    }

    // Warning logging methods
    static void warn(const std::string& message) {
        logMessage("Warning: ", message, Verbosity::NORMAL);
    }

    static void warn(const std::vector<std::string>& messages, bool clearMessages = false) {
        logMessages("Warning: ", messages, clearMessages, Verbosity::NORMAL);
    }

    // Error logging methods
    static void error(const std::string& message) {
        logMessage("ERROR: ", message, Verbosity::NORMAL);
    }

    static void error(const std::vector<std::string>& messages, bool clearMessages = false) {
        logMessages("ERROR: ", messages, clearMessages, Verbosity::NORMAL);
    }

    // Fatal error logging methods
    static void fatal(const std::string& message) {
        labeledMessage("FATAL: ", message);
        std::string timestamp = getTimestamp();
        info("Performance solver terminated abnormally on " + timestamp);
        finalize();
        MPI_Abort(MPI_COMM_WORLD, 1);
        std::exit(1);
    }

    static void fatal(const std::vector<std::string>& messages) {
        labeledMessages("FATAL: ", messages);
        std::string timestamp = getTimestamp();
        info("Performance solver terminated abnormally on " + timestamp);
        finalize();
        MPI_Abort(MPI_COMM_WORLD, 1);
        std::exit(1);
    }

    // Normal exit method
    static void exit() {
        std::string timestamp = getTimestamp();
        info("");
        info("Performance solver terminated normally on " + timestamp);
        finalize();
        std::exit(0);
    }

    // Panic method for immediate termination
    static void panic(const std::string& message) {
        int rank;
        MPI_Comm_rank(MPI_COMM_WORLD, &rank);
        std::cerr << "PANIC [Rank " << rank << "]: " << message << std::endl;
        std::string timestamp = getTimestamp();
        std::cerr << "Performance solver terminated abnormally on " << timestamp << std::endl;
        MPI_Abort(MPI_COMM_WORLD, 1);
        std::exit(1);
    }

private:
    static std::mutex mutex_;
    static bool initialized_;
    static bool isIOP_;
    static std::string prefix_;
    static Verbosity verbosity_;
    static std::vector<std::ostream*> logUnits_;
    static std::ofstream logFile_;

    // Core logging methods
    static void logMessage(const std::string& label, const std::string& message, 
                         Verbosity verbosity) {
        std::lock_guard<std::mutex> lock(mutex_);
        
        if (!initialized_) return;
        if (verbosity > verbosity_) return;
        if (!isIOP_) return;

        for (auto& stream : logUnits_) {
            *stream << label << message << std::endl;
        }
    }

    static void logMessages(const std::string& label, const std::vector<std::string>& messages,
                          bool clearMessages, Verbosity verbosity) {
        std::lock_guard<std::mutex> lock(mutex_);
        
        if (!initialized_) return;
        if (verbosity > verbosity_) return;
        if (!isIOP_) return;
        if (messages.empty()) return;

        // Log first message with label
        for (auto& stream : logUnits_) {
            *stream << label << messages[0] << std::endl;
        }

        // Log remaining messages indented
        for (size_t i = 1; i < messages.size(); ++i) {
            for (auto& stream : logUnits_) {
                *stream << std::string(label.length(), ' ') << messages[i] << std::endl;
            }
        }
    }

    static void labeledMessage(const std::string& label, const std::string& message) {
        logMessage(label, message, Verbosity::NORMAL);
    }

    static void labeledMessages(const std::string& label, const std::vector<std::string>& messages) {
        logMessages(label, messages, false, Verbosity::NORMAL);
    }

    // Helper method to get current timestamp
    static std::string getTimestamp() {
        auto now = std::time(nullptr);
        auto tm = *std::localtime(&now);
        std::ostringstream oss;
        oss << std::put_time(&tm, "%b %d at %H:%M:%S");
        return oss.str();
    }
};

// Static member initialization
std::mutex LoggingService::mutex_;
bool LoggingService::initialized_ = false;
bool LoggingService::isIOP_ = false;
std::string LoggingService::prefix_;
LoggingService::Verbosity LoggingService::verbosity_ = LoggingService::Verbosity::NORMAL;
std::vector<std::ostream*> LoggingService::logUnits_;
std::ofstream LoggingService::logFile_;